/*
 * Unidata Platform Community Edition
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 * This file is part of the Unidata Platform Community Edition software.
 *
 * Unidata Platform Community Edition is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Unidata Platform Community Edition is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

package org.unidata.mdm.data.service.segments.records.upsert;

import java.util.Objects;
import java.util.UUID;

import org.springframework.stereotype.Component;
import org.unidata.mdm.core.po.lob.BinaryLargeObjectPO;
import org.unidata.mdm.core.po.lob.CharacterLargeObjectPO;
import org.unidata.mdm.core.po.lob.LargeObjectPO;
import org.unidata.mdm.core.type.calculables.CalculableHolder;
import org.unidata.mdm.core.type.data.Attribute;
import org.unidata.mdm.core.type.data.Attribute.AttributeType;
import org.unidata.mdm.core.type.data.SimpleAttribute;
import org.unidata.mdm.core.type.data.SimpleAttribute.SimpleDataType;
import org.unidata.mdm.core.type.lob.LargeObjectAcceptance;
import org.unidata.mdm.data.context.UpsertRequestContext;
import org.unidata.mdm.data.module.DataModule;
import org.unidata.mdm.data.service.segments.LobSubmitSupport;
import org.unidata.mdm.data.type.apply.RecordUpsertChangeSet;
import org.unidata.mdm.data.type.data.OriginRecord;
import org.unidata.mdm.data.type.data.UpsertAction;
import org.unidata.mdm.system.type.pipeline.Point;
import org.unidata.mdm.system.type.pipeline.Start;
import org.unidata.mdm.system.type.runtime.MeasurementPoint;

/**
 * @author Mikhail Mikhailov
 */
@Component(RecordUpsertLobSubmitExecutor.SEGMENT_ID)
public class RecordUpsertLobSubmitExecutor extends Point<UpsertRequestContext> implements LobSubmitSupport {
    /**
     * This segment ID.
     */
    public static final String SEGMENT_ID = DataModule.MODULE_ID + "[RECORD_UPSERT_LOB_SUBMIT]";
    /**
     * Localized message code.
     */
    public static final String SEGMENT_DESCRIPTION = DataModule.MODULE_ID + ".record.upsert.lob.submit.description";
    /**
     * Constructor.
     */
    public RecordUpsertLobSubmitExecutor() {
        super(SEGMENT_ID, SEGMENT_DESCRIPTION);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void point(UpsertRequestContext ctx) {

        if (ctx.upsertAction() == UpsertAction.NO_ACTION) {
            return;
        }

        // Only first level supported
        MeasurementPoint.start();
        try {

            CalculableHolder<OriginRecord> holder = ctx.modificationBox().peek(ctx.toBoxKey());
            if (Objects.isNull(holder) || Objects.isNull(holder.getValue())) {
                return;
            }

            RecordUpsertChangeSet set = ctx.changeSet();
            holder.getValue().getAttributesRecursive().stream()
                .filter(attr -> attr.getAttributeType() == AttributeType.SIMPLE)
                .map(Attribute::<SimpleAttribute<?>>narrow)
                .filter(attr -> attr.getDataType() == SimpleDataType.BLOB || attr.getDataType() == SimpleDataType.CLOB)
                .forEach(attr -> scheduleAccept(attr, set, holder));

        } finally {
            MeasurementPoint.stop();
        }
    }
    /**
     * Schedules accept, if this attribute's value has not benn accepted yet.
     * @param attr the attribute
     * @param set change set
     * @param origin the owner origin
     */
    private void scheduleAccept(Attribute attr, RecordUpsertChangeSet set, CalculableHolder<OriginRecord> origin) {

        SimpleAttribute<?> lobAttr = attr.narrow();
        boolean isBinary = lobAttr.getDataType() == SimpleDataType.BLOB;

        String objectId = isBinary ? getBlobObjectId(lobAttr) : getClobObjectId(lobAttr);
        if (objectId == null) {
            return;
        }

        LargeObjectAcceptance state = isBinary ? getBlobAcceptance(lobAttr) : getClobAcceptance(lobAttr);
        if (state != LargeObjectAcceptance.ACCEPTED) {

            LargeObjectPO po = isBinary ? new BinaryLargeObjectPO() : new CharacterLargeObjectPO();
            po.setId(UUID.fromString(objectId));
            po.setSubject(origin.getValue().getInfoSection().getOriginKey().getId());
            po.setState(LargeObjectAcceptance.ACCEPTED);

            set.getLargeObjectPOs().add(po);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supports(Start<?, ?> start) {
        return UpsertRequestContext.class.isAssignableFrom(start.getInputTypeClass());
    }
}
